package com.github.codemperor.core.schedule;

import com.alibaba.druid.util.StringUtils;
import com.github.codemperor.core.util.SpringContextUtil;
import com.github.codemperor.core.schedule.db.EmSchedulerManager;
import com.github.codemperor.core.schedule.db.EmSchedulerManagerMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.support.CronTrigger;

import java.util.function.Consumer;

@Getter
@Setter
@Slf4j
public abstract class BaseEmScheduler {
    private Runnable task;
    private Trigger trigger;

    /**
     * 配置信息
     *
     * @return
     */
    protected abstract Config config();

    /**
     * 子类业务实现
     *
     * @return
     */
    protected abstract void service(EmSchedulerManager emSchedulerManager);

    private EmSchedulerManagerMapper mapper() {
        return SpringContextUtil.getBean(EmSchedulerManagerMapper.class);
    }

    /**
     * 初始化并判断job的数据库配置
     */
    private EmSchedulerManager jobDatabaseInit(Config config) {
        EmSchedulerManager emSchedulerManager = mapper().selectOne(new EmSchedulerManager().setClassz(config.getClassz().getName()));
        if (emSchedulerManager == null || emSchedulerManager.getId() == null) {
            emSchedulerManager = new EmSchedulerManager();
            emSchedulerManager.setAwait(false)
                    .setCron(config.getCron())
                    .setClassz(config.getClassz().getName())
                    .setDescription(config.getDesc())
                    .setActive(true);
            mapper().insertSelective(emSchedulerManager);
        }

        //将配置替换为数据库
        config.setDesc(emSchedulerManager.getDescription())
                .setCron(emSchedulerManager.getCron());

        return emSchedulerManager;
    }


    /**
     * 业务锁环绕，执行方法前锁的判定以及解锁操作
     *
     * @return
     */
    private void serviceLockAround(EmSchedulerManager dbConfig, Consumer<Config> function) {
        boolean await = false;
        try {
            //先上锁,并上锁成功,才可以执行业务
            if (mapper().jobLock(dbConfig.getClassz()) > 0) {
                await = true;
                function.accept(config());
            } else {
                log.error("[{}|{}] lock failed, it has already running", dbConfig.getClassz(), dbConfig.getDescription());
            }
        } catch (Exception e) {
            log.error("", e);
        } finally { //最终更新回未执行状态
            //只有上面加过锁并执行过逻辑后，才需要释放锁，否则直接释放，会造成正在执行的业务锁被释放掉
            if (await) {
                log.info("[{}|{}] unlock, finish service", dbConfig.getClassz(), dbConfig.getDescription());
                mapper().updateByPrimaryKeySelective(new EmSchedulerManager().setId(dbConfig.getId()).setAwait(false));
            }
        }
    }

    /**
     * 初始化job信息配置
     */
    public void init() {
        Config config = config();
        if (config == null || config.getClassz() == null || StringUtils.isEmpty(config.getCron())) {
            return;
        }
        //初始化所有job的配置，并载入数据库，如果已存在则无需改变
        jobDatabaseInit(config);

        //初始化任务
        task = () -> {
            //判断是否在执行中,加锁解锁
            EmSchedulerManager emSchedulerManager = mapper().selectOne(new EmSchedulerManager().setClassz(config().getClassz().getName()));
            serviceLockAround(emSchedulerManager, (c) -> service(emSchedulerManager));
        };

        //初始化任务的执行周期，目前仅支持cron
        trigger = (triggerContext) -> {
            //这里更新后，只有下一次执行后才生效
            EmSchedulerManager emSchedulerManager = mapper().selectOne(new EmSchedulerManager().setClassz(config().getClassz().getName()));
            CronTrigger cronTrigger = new CronTrigger(emSchedulerManager.getCron());
            return cronTrigger.nextExecutionTime(triggerContext);
        };
    }

    /**
     * 直接执行
     */
    public void run() {
        init();
        if (null != task) {
            task.run();
        }
    }

    /**
     * 配置
     */
    @Getter
    @Setter
    public static class Config {
        /**
         * 子类
         */
        private Class<?> classz;

        /**
         * 描述
         */
        private String desc;

        /**
         * 执行的周期，cron表达式
         */
        private String cron;
    }
}
